#pragma once
#include <iostream>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "err.hpp"
using namespace std;
// 服务器
namespace udp_server
{
    const static uint16_t DefaultPort = 8080;
    class UdpServer
    {
    public:
        // 构造
        UdpServer(uint16_t port = DefaultPort) : port_(port)
        {
            cout << "server addr:" << port_ << endl;
        }
        /*初始化*/
        void InitServer()
        {
            // 1.创建socket套接字--打开网络文件
            sock_ = socket(AF_INET, SOCK_DGRAM, 0); // AF_INET==PF_INET:ipv4地址族 SOCK_DGRAM:数据报套接字,用于UDP
            if (sock_ == -1)                        /*创建失败*/
            {
                cerr << "socket create error：" << strerror(errno) << endl;
                exit(SOCKET_ERROR); // 退出->退出码：SOCKET_ERROR
            }
            cout << "socket create success：" << sock_ << endl;
            // 2.给服务器绑定IP地址和端口号
            /*int bind(int socket, const struct sockaddr *address,socklen_t address_len);*/
            struct sockaddr_in local;     // local局部变量，用户空间的特定函数的栈帧上，不在内核中
            bzero(&local, sizeof(local)); // 初始化为0
            local.sin_family = AF_INET;   // 网络通信
            /*端口号
            客户端和服务器在进行网络通信时，需要知道相互之间ip和端口号，也就是说在网络通信传输数据中存在端口号这个数据
            所以需要将数据由主机序列变成网络序列
            端口号-->typedef uint16_t in_port_t
            用htons函数将端口号进行大小端的转化，
            */
            local.sin_port = htons(port_);
            /*IP地址
              1)将字符串风格的IP地址，转化成为4字节int，"1.1.1.1"->uint32_t->能不能强制类型转换？不能，这里需要转化
              2)需要将数据由主机序列变成网络序列
              直接使用inet_addr函数完成以上两种功能
              3)云服务器不需要自己绑定IP，只需要传端口号port
            */
            // local.sin_addr.s_addr = inet_addr(ip_.c_str());            //IP地址
            local.sin_addr.s_addr = INADDR_ANY; // 让我们udpserver在启动的时候，bind本主机上的任意IP
            if (bind(sock_, (struct sockaddr *)&local, sizeof(local)) < 0)
            {
                cerr << "bind error：" << strerror(errno) << endl;
                exit(BIND_ERROR);
            }
            // cout<<"bind success："<<local.sin_addr.s_addr<<local.sin_port<<endl;
            cout << "bind success：" << sock_ << endl;
        }
        /*启动*/
        void Start()
        {
            char buff[1024]; // 存储客户端发来的数据的缓冲区
            //cout << "Start success" << endl;
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            memset(&peer, 0, sizeof(peer)); // peer里面数据乱码-->初始化
            while (true)                    // 一直运行
            {
                /*ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);*/
                // 1.接收客户端的数据--flags取0阻塞式接收
                //cout << "before server receive massage success " << endl;
                ssize_t n = recvfrom(sock_, buff, sizeof(buff) - 1, 0, (struct sockaddr *)&peer, &len);
                if (n > 0)
                {
                    buff[n] = 0; // 数据传输是字节流，要变成字符串，在末尾加上'/0'即0
                }
                else
                    continue;
                //cout << "server receive massage success " << endl;
                // 打印数据
                // 获取客户端的IP和端口号port
                string clientip = inet_ntoa(peer.sin_addr); // 将客户端的IP由网络序列变成主机序列
                uint16_t clientport = ntohs(peer.sin_port); // 将客户端的端口号port由网络序列变成主机序列
                cout << clientip << "-" << clientport << "#" << buff << endl;
                // 2.将客户端发来的数据转发回去
                sendto(sock_, buff, strlen(buff), 0, (struct sockaddr *)&peer, len);
                //cout << "server send to massage success " << endl;
            }
        }
        // 析构
        ~UdpServer()
        {
        }

    private:
        int sock_;      // 套接字接口文件标识符
        uint16_t port_; // 端口号
    };
}